#include <list>
#include "Level.h"
#include <iostream>
#include <fstream>

Level::Level(int width, int height, D3DVIEWPORT9 viewport, TileSet* tileset,
	D3DXVECTOR3 startPoint, D3DXVECTOR3 endPoint, D3DXVECTOR3 offset)
{
	this->width = width;
	this->height = height;
	this->viewport = viewport;
	this->tileset = tileset;
	this->squares = new LevelSquare*[width];
	for (int i = 0; i < width; ++i)
		squares[i] = new LevelSquare[height];
	this->ID = -1;
	this->startPoint = startPoint;
	this->endPoint = endPoint;
	this->offset = offset;
	this->initOffset = offset;
}

Level::~Level()
{
	for (int x = 0; x < width; ++x)
	{
		delete[] squares[x];
	}
	delete[] squares;
}

void Level::setSquare(int x, int y, LevelSquare square)
{
	squares[x][y] = square;
}

void Level::draw(ID3DXSprite* sprite)
{
	int startX = (int)offset.x / tileset->getTileWidth();
	int endX = ((int)offset.x + viewport.Width) / tileset->getTileWidth();
	int startY = (int)offset.y / tileset->getTileHeight();
	int endY = ((int)offset.y + viewport.Height) / tileset->getTileHeight();

	if (startX < 0)
		startX = 0;
	if (endX > width - 1)
		endX = width - 1;
	if (startY < 0)
		startY = 0;
	if (endY > height - 1)
		endY = height -1;

	for (int x = startX; x <= endX; ++x)
	{
		for (int y = startY; y <= endY; ++y)
		{
			if (squares[x][y].isVisible())
				squares[x][y].draw(sprite, offset);
		}
	}
}

D3DXVECTOR3 Level::checkCollision(Entity* entity, BoundingBox& bounds, D3DXVECTOR3 movement)
{
	// Get the position of the entity
	D3DXVECTOR3 position = entity->getPosition() + offset;
	position.y += movement.y;

	// Find tiles to check
	int startX = (int)position.x / tileset->getTileWidth();
	int endX = ((int)position.x + bounds.size.x) / tileset->getTileWidth();
	int startY = (int)position.y / tileset->getTileHeight();
	int endY = ((int)position.y + bounds.size.y) / tileset->getTileHeight();

	// Make sure we only check tiles that exist
	if (startX < 0)
		startX = 0;
	if (endX > width - 1)
		endX = width - 1;
	if (startY < 0)
		startY = 0;
	if (endY > height - 1)
		endY = height - 1;

	// Set tile size
	D3DXVECTOR3 tileSize(tileset->getTileWidth(), tileset->getTileHeight(), 0);

	// Check all potential colliding squares
	for (int x = startX; x <= endX; ++x)
	{
		for (int y = startY; y <= endY; ++y)
		{
			// If square doesn't exist, or is passable, ignore
			if (!squares[x][y].isVisible() || squares[x][y].isPassable())
				continue;
			// Creat tile bounds
			BoundingBox tileBounds = { squares[x][y].getPosition(), tileSize };

			// Create temporory bounding box
			BoundingBox tempBounds = { position, bounds.size };

			// Test collision
			if (TestCollision(tempBounds, tileBounds))
			{
				if (movement.y > 0)
					position.y = tileBounds.position.y - bounds.size.y - 1;
				else
					position.y = tileBounds.position.y + tileBounds.size.y + 1;
				// We fudge the movement by one pixel to avoid the collision from
				// being true again.
			}
		}
	}

	// Do the same for X movement
	position.x += movement.x;

	// Find tiles to check
	startX = (int)position.x /  tileset->getTileWidth();
	endX = ((int)position.x + bounds.size.x) / tileset->getTileWidth();
	startY = (int)position.y / tileset->getTileHeight();
	endY = ((int)position.y + bounds.size.y) / tileset->getTileHeight();

	// Make sure we only check tiles that exist
	if (startX < 0)
		startX = 0;
	if (endX > width - 1)
		endX = width - 1;
	if (startY < 0)
		startY = 0;
	if (endY > height - 1)
		endY = height - 1;

	// Check all potential colliding squares
	for (int x = startX; x <= endX; ++x)
	{
		for (int y = startY; y <= endY; ++y)
		{
			// If square doesn't exist, or is passable, ignore
			if (!squares[x][y].isVisible() || squares[x][y].isPassable())
				continue;
			// Create tile bounds
			BoundingBox tileBounds = { squares[x][y].getPosition(), tileSize };
			// Create temporary bounding box
			BoundingBox tempBounds = { position, bounds.size };
			// Test collision
			if (TestCollision(tempBounds, tileBounds))
			{
				if (movement.x > 0)
					position.x = tileBounds.position.x - bounds.size.x - 1;
				else if (movement.x < 0)
					position.x  = tileBounds.position.x + tileBounds.size.x + 1;
				// We fudger the movement by one pixel to avoid the collision from
				// being true again.
			}
		}
	}
	// Return new movement
	return position - offset;
}

Level* Level::createLevel(TileSet* tileSet, std::string path,
	D3DVIEWPORT9 viewport, D3DXVECTOR3 startPoint, D3DXVECTOR3 endPoint, D3DXVECTOR3 offset)
{
	int width;
	std::list<std::string> lines;

	std::ifstream file(path.c_str());
	if (file.is_open())
	{
		std::string line;
		std::getline(file, line);
		width = line.length();
		if (width % 2 != 0)
			throw; // Length of line incorrect
		while (file.good())
		{
			lines.push_back(line);
			if (line.length() != width)
				throw; // Line doesn't match length of others
			std::getline(file, line);
		}

		Level* level = new Level(width / 2, lines.size(), viewport, tileSet,
			startPoint, endPoint, offset);

		for (int y = 0; lines.size() > 0; ++y)
		{
			std::string currentLine = lines.front();
			lines.pop_front();
			for (int x = 0; x < width; x = x + 2)
			{
				char passable = currentLine[x];
				bool isPassable;

				if (passable == '0')
					isPassable = true;
				else
					isPassable = false;

				char tileName = currentLine[x + 1];
				if (tileName == '.')
					continue; // LevelSquare is clear
				else
				{
					D3DXVECTOR3 position((x / 2) * tileSet->getTileWidth(), y * tileSet->getTileHeight(), 0);
					LevelSquare square = LevelSquare(isPassable, position, tileSet->getTile(tileName));
					level->setSquare(x / 2, y, square);
				}
			}
		}
		return level;
	}
	else
		throw; // File doesn't exist
}